package com.dridco;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.dridco.exception.GrafoException;

public class CalculoImpl implements Calculo{
	
	@Override
    public int calcDistancia(Map<Nodo,Integer> grafo, String recorrido) throws GrafoException{
    	String[] cuidades = recorrido.split("-");
    	
    	int i = 1;
    	Resultado result = new Resultado();
    	
    	while (i < cuidades.length){
    		buscarDistancia(grafo, cuidades[i-1].charAt(0),cuidades[i].charAt(0),result);
    		i++;
    	}
   		return result.getResultado();
    }

	private void buscarDistancia(Map<Nodo, Integer> grafo, char desde, char hasta, Resultado result) throws GrafoException {
		Iterator<Nodo> it = grafo.keySet().iterator();
		if (it.hasNext()) {
			boolean encontrado = false;
			while (it.hasNext() && !encontrado) {
				Nodo nodo = it.next();
				if (nodo.getDesde() == desde) {
					if (nodo.getHasta() == hasta) {
						encontrado = true;
						result.setResultado(result.getResultado() + grafo.get(nodo));
					}
				}
			}
			if (!encontrado)
				throw new GrafoException("SIN RUTA");
		}
	}
	
	@Override
	public int calcCantViajes(Map<Nodo, Integer> grafo, String recorrido, int maxParadas) throws GrafoException {
		String[] cuidades = recorrido.split("-");

		int i = 1, paradas =0;
		Resultado result = new Resultado();

		while (i < cuidades.length) {
			
			buscarCantViajes(grafo, cuidades[i - 1].charAt(0), cuidades[i].charAt(0),paradas,maxParadas, result);
			i++;
		}
		
		return result.getResultado();
	}
		  
	private void buscarCantViajes(Map<Nodo, Integer> grafo, char desde, char hasta, int paradas, int maxParadas, Resultado result) throws GrafoException{
		paradas++;
		if (paradas > maxParadas){
			throw new GrafoException("Limite de pasadas es de " + maxParadas);
		}
		Iterator<Nodo> it = grafo.keySet().iterator();
		if (it.hasNext()) {
			boolean encontrado = false;
			List<Nodo> lista = new LinkedList<Nodo>();
			while (it.hasNext() && !encontrado) {
				Nodo nodo = it.next();
				if (nodo.getDesde() == desde) {
					if (nodo.getHasta() == hasta) {
						encontrado = true;
					} else {
						lista.add(nodo);
					}

				}
			}

			if (encontrado)
				result.setResultado(result.getResultado() + 1);

			for (Nodo nodo : lista) {
				buscarCantViajes(grafo, nodo.getHasta(), hasta, paradas, maxParadas, result);
			}
		}
	}
	
	@Override
	public int calcCantViajesExacto(Map<Nodo, Integer> grafo, String recorrido, int maxParadas) throws GrafoException {
		String[] cuidades = recorrido.split("-");

		int i = 1, paradas = 0;
		Resultado result = new Resultado();

		while (i < cuidades.length) {
			
			buscarCantViajesExacto(grafo, cuidades[i - 1].charAt(0), cuidades[i].charAt(0),paradas, maxParadas, result);
			i++;
		}
		
		return result.getResultado();
	}

	
	private void buscarCantViajesExacto(Map<Nodo, Integer> grafo, char desde, char hasta, int paradas, int maxParadas, Resultado result)throws GrafoException{
		paradas++;
		Iterator<Nodo> it = grafo.keySet().iterator();
		if (it.hasNext()) {
			boolean encontrado = false;
			List<Nodo> lista = new LinkedList<Nodo>();
			while (it.hasNext()) {
				Nodo nodo = it.next();
				if (nodo.getDesde() == desde) {
					if (nodo.getHasta() == hasta) {
						encontrado = true;
					}
					lista.add(nodo);
				}
			}
			
			if (encontrado && paradas == maxParadas){
				result.setResultado(result.getResultado() + 1);
			} 
			if (paradas < maxParadas){				
				for (Nodo nodo : lista) {
					buscarCantViajesExacto(grafo, nodo.getHasta(), hasta, paradas, maxParadas, result);
				}
			}
		}
	}
	
	@Override
	public int calcDistaciaMasCorta(Map<Nodo, Integer> grafo, String recorrido) throws GrafoException {
		String[] cuidades = recorrido.split("-");

		int i = 1, maximo = 0;
		Resultado result = new Resultado();
		result.setResultado(9999);
		while (i < cuidades.length) {
			
			buscarDistanciaMasCorta(grafo, cuidades[i - 1].charAt(0), cuidades[i].charAt(0), '2', maximo, result);
			i++;
		}
		
		return result.getResultado();
		
	}
	
	private void buscarDistanciaMasCorta(Map<Nodo, Integer> grafo, char desde, char hasta, char anterior, int maximo, Resultado result) throws GrafoException {
		Iterator<Nodo> it = grafo.keySet().iterator();
		if (it.hasNext()) {
			boolean encontrado = false;
			List<Nodo> lista = new LinkedList<Nodo>();
			while (it.hasNext() && !encontrado) {
				Nodo nodo = it.next();
				if (nodo.getDesde() == desde && nodo.getHasta() != anterior) {
					if (nodo.getHasta() == hasta) {
						maximo += grafo.get(nodo);
						encontrado = true;
					} else {
						lista.add(nodo);
					}

				}
			}

			if (encontrado)				
				if ( result.getResultado() > maximo)
					result.setResultado(maximo);

			for (Nodo nodo : lista) {
				maximo += grafo.get(nodo);
				anterior = nodo.getDesde();
				buscarDistanciaMasCorta(grafo, nodo.getHasta(), hasta,anterior, maximo, result);
				maximo -= grafo.get(nodo);
			}
		}
	}
	
	@Override	
	public int calcCantRutas(Map<Nodo, Integer> grafo, String recorrido, int distanciaMaxima) throws GrafoException {
		String[] cuidades = recorrido.split("-");

		int i = 1;
		Resultado result = new Resultado();
		
		while (i < cuidades.length) {
			buscarCantRutas(grafo, cuidades[i - 1].charAt(0), cuidades[i].charAt(0), 0, distanciaMaxima, result);
			i++;
		}
		
		return result.getResultado();
	}
	
	private void buscarCantRutas(Map<Nodo, Integer> grafo, char desde, char hasta, int distancia, int distanciaMaxima, Resultado result) throws GrafoException {
		Iterator<Nodo> it = grafo.keySet().iterator();
		if (it.hasNext()) {
			while (it.hasNext()) {
				Nodo nodo = it.next();
				distancia += grafo.get(nodo);				
				if (distancia < distanciaMaxima && nodo.getDesde() == desde) {
					if (nodo.getHasta() == hasta) {
						result.setResultado(result.getResultado() + 1);
					}
					buscarCantRutas(grafo, nodo.getHasta(), hasta, distancia, distanciaMaxima, result);
						
				}
				distancia -= grafo.get(nodo);
			}
		}
	}
	
}
